﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Poker24Demo
{
    public sealed class Poker24
    {
        private string[] poker = new string[4];
        private string[] signArr = new string[4] { "+", "-", "*", "/" };

        public Poker24()
        {

        }

        public void DealFourCards(string pokerA, string pokerB, string pokerC, string pokerD)
        {
            poker[0] = pokerA;
            poker[1] = pokerB;
            poker[2] = pokerC;
            poker[3] = pokerD;
        }

        public List<CalcBlock> Calc24()
        {

            List<CalcBlock> pbs = new List<CalcBlock>();
            List<string[]> list = new List<string[]>();

            //4个数字共有4*3*2*1=24种排列组合
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (i == j) continue;
                    for (int k = 0; k < 4; k++)
                    {
                        if (i == k || j == k) continue;
                        for (int m = 0; m < 4; m++)
                        {
                            if (i == m || j == m || k == m) continue;
                            list.Add(new string[4] { poker[i], poker[j], poker[k], poker[m] });
                        }
                    }
                }
            }

            //三个=-*/位置共有4*4*4=64种组合
            List<string[]> signList = new List<string[]>();
            for (int i = 0; i < signArr.Length; i++)
            {
                for (int j = 0; j < signArr.Length; j++)
                {
                    for (int k = 0; k < signArr.Length; k++)
                    {
                        signList.Add(new string[3] { signArr[i], signArr[j], signArr[k] });
                    }
                }
            }

            //数字和符号位 组合共有 24*64=1536种组合
            List<Tuple<string[], string[]>> formulaList = new List<Tuple<string[], string[]>>();
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = 0; j < signList.Count; j++)
                {
                    formulaList.Add(new Tuple<string[], string[]>(list[i], signList[j]));
                }
            }
            //共有6中括号位置可能  共有1536*6=9216种组合
            foreach (var item in formulaList)
            {
                string[] a = item.Item1;
                string[] b = item.Item2;
                
                //筛选结果等于24的计算公式
                var af1 = AnalyticFormula($"{a[0]}{b[0]}{a[1]}{b[1]}{a[2]}{b[2]}{a[3]}");
                if (af1.Calc() == 24) pbs.Add(af1);

                af1 = AnalyticFormula($"({a[0]}{b[0]}{a[1]}){b[1]}{a[2]}{b[2]}{a[3]}");
                if (af1.Calc() == 24) pbs.Add(af1);

                af1 = AnalyticFormula($"({a[0]}{b[0]}{a[1]}{b[1]}{a[2]}){b[2]}{a[3]}");
                if (af1.Calc() == 24) pbs.Add(af1);

                af1 = AnalyticFormula($"{a[0]}{b[0]}({a[1]}{b[1]}{a[2]}){b[2]}{a[3]}");
                if (af1.Calc() == 24) pbs.Add(af1);

                af1 = AnalyticFormula($"{a[0]}{b[0]}({a[1]}{b[1]}{a[2]}{b[2]}{a[3]})");
                if (af1.Calc() == 24) pbs.Add(af1);

                af1 = AnalyticFormula($"{a[0]}{b[0]}{a[1]}{b[1]}({a[2]}{b[2]}{a[3]})");
                if (af1.Calc() == 24) pbs.Add(af1);

            }

            //排除重复的计算公式
            pbs = pbs.Distinct(new CalcPokerBlockComparer()).ToList();
            //交换律排除重复的计算公式
            ExcludeOfExchange(pbs);
            return pbs;

        }

        /// <summary>
        /// 分析计算公式，将字符串计算公式解析为CalcBlock
        /// </summary>
        private CalcBlock AnalyticFormula(string formula)
        {

            var b = formula.ToArray();

            Stack<PokerBlock> stacks = new Stack<PokerBlock>();
            List<PokerBlock> list = new List<PokerBlock>();

            string c = "";
            for (var i = 0; i < b.Length; i++)
            {
                if (char.IsDigit(b[i]))
                {
                    c += b[i];

                    if (i == b.Length - 1) list.Add(new ValueBlock(double.Parse(c)));
                }
                else
                {
                    if (c != "") list.Add(new ValueBlock(double.Parse(c)));
                    list.Add(new SignBlock(b[i].ToString()));
                    c = "";
                }
            }
            //利用栈逐步解析公式
            int signCount = 0;
            SignBlock preSign = null;
            foreach (var item in list)
            {
                var sb = item as SignBlock;
                if (sb != null) //符号
                {
                    if (sb.IsRightBracket)
                    {
                        if (preSign != null && signCount > 1)
                        {
                            SignPop(stacks);

                        }
                        RightBracketPop(stacks);
                        preSign = null;
                        signCount = 0;
                    }
                    else if (sb.IsLeftBracket)
                    {
                        preSign = null;
                        signCount = 0;
                        stacks.Push(item);
                    }
                    else
                    {
                        if (preSign == null)
                        {
                            preSign = sb;
                            signCount++;
                            stacks.Push(item);
                        }
                        else
                        {
                            if (sb.ComparePriority(preSign) <= 0)
                            {
                                preSign = sb;
                                signCount = 1;
                                SignPop(stacks);
                                stacks.Push(item);
                            }
                            else
                            {
                                preSign = sb;
                                signCount++;
                                stacks.Push(item);
                            }
                        }
                    }
                }
                else
                { //数字
                    stacks.Push(item);
                }

            }

            End(stacks);
            var res = stacks.Pop();
            return res as CalcBlock;
        }

        /// <summary>
        /// 右括号出栈
        /// </summary>
        private void RightBracketPop(Stack<PokerBlock> stacks)
        {
            var d = stacks.Pop(); // 3
            var c = stacks.Pop(); // +
            var b = stacks.Pop(); // 5
            var a = stacks.Pop(); // )

            var e = c as SignBlock;
            var f = e.Merge(b as CalcBlock, d as CalcBlock);
            stacks.Push(f);

        }

        // +-*/符号出栈
        private void SignPop(Stack<PokerBlock> stacks)
        {
            var c = stacks.Pop(); // 3
            var b = stacks.Pop(); // +
            var a = stacks.Pop(); // 5

            var d = b as SignBlock;
            var e = d.Merge(a as CalcBlock, c as CalcBlock);
            stacks.Push(e);

        }

        /// <summary>
        /// 最后全部出栈
        /// </summary>
        private void End(Stack<PokerBlock> stacks)
        {
            if (stacks.Count == 1)
            {
                return;
            }

            var c = stacks.Pop(); // 3
            var b = stacks.Pop(); // +
            var a = stacks.Pop(); // 5

            var d = b as SignBlock;
            var e = d.Merge(a as CalcBlock, c as CalcBlock);
            stacks.Push(e);
            End(stacks);

        }

        /// <summary>
        /// 交换律排除重复
        /// </summary>
        private void ExcludeOfExchange(List<CalcBlock> pbs)
        {
            List<CalcBlock> removePbs = new List<CalcBlock>();

            for (int i = 0; i < pbs.Count - 1; i++)
            {
                string exchangeFormula = pbs[i].LawOfExchange();
                if (exchangeFormula == null) continue;
                for (int j = i + 1; j < pbs.Count; j++)
                {
                    string formula = pbs[j].Formula();
                    if (exchangeFormula == formula)
                    {
                        removePbs.Add(pbs[j]);
                    }
                }
            }

            if (removePbs.Count > 0) removePbs.ForEach(item => { pbs.Remove(item); });
        }


    }
}
